<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

class f_document_Provider
{
	private static $instance;
	private $class = null;
	private $tableName = null;
	private $document_model = null;
	
	private function __construct()
	{
		// Do nothing for the moment...
	}

	/**
	 * @return f_document_Provider
	 */
	public static function getInstance()
	{
		if(is_null(self::$instance))
		{
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Get and retrieve a document with the given Id
	 *
	 * @param integer $id
	 * @param string $lang
	 * @return f_document_BaseDocument
	 */
	public function getByUniqueId($id, $lang = null)
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getLang();
		}
		
		$cache = f_core_Cache::getInstance();
		
		$key = $cache->getDocumentCacheKey($id, $lang);
		
		if($cache->hasCache($key))
		{
			return $cache->getCacheContent($key);
		}
		
		$document = $this->getDocumentFromTableById("f_document", $id);

		if($document != false)
		{
			$className = $this->resolveClassName($document['document_model']);

			$object = new $className($id, $lang);
			
			$cache->putInCache($key, $object);
			
			return $object;
		}

		throw new Exception("No document with id=".$id);
	}
	
	/**
	 * Get document instance by model, id and lang
	 *
	 * @param string $documentModel
	 * @param integer $id
	 * @param string $lang
	 */
	public function getByModelAndUniqueId($documentModel, $id, $lang = null)
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getLang();
		}

		$className = $this->resolveClassName($documentModel);
		$doc = call_user_func(array($className, 'getInstanceByUniqueId'), $id, $lang);
		
		if($doc === false)
		{
			throw new Exception("Document $documentModel/$id/$lang cannot be load");
		}
		
		return $doc;
	}
	
	/**
	 * Retrieve all revision documents by id
	 *
	 * @param integer $id
	 * @return array
	 */
	public function getRevisionDocumentsById($id, $lang = null)
	{		
		$cache = f_core_Cache::getInstance();

		if(is_null($lang)) $lang = f_core_Context::getInstance()->getLang();
				
		$document = $this->getByUniqueId($id, $lang);		
		
		return $this->getRevisionDocuments($document);
	}
	
	/**
	 * Retrieve all revisions for a given document
	 * @return array
	 * @param f_document_BaseDocument $document
	 */
	public function getRevisionDocuments($document)
	{	
		$tableName = $document->getTableName()."_revisions";
		
		if($document->isLocalized() && $document->isATranslation())
		{
			$tableName = $tableName."_i18n";
		}
		
		$dbProvider = f_database_Provider::getInstance();
		
		$revisions = $dbProvider->setQuery("SELECT `document_revision` FROM `".$tableName."` WHERE `document_uid`=:id AND `document_lang`=:lang")->execute(array("id" => $document->getUniqueId(), "lang" => $document->getLang()))->getQueryResults(PDO::FETCH_COLUMN);

		$results = array();	
		
		if(count($revisions) > 1)
		{
			$cache = f_core_Cache::getInstance();
			
			foreach($revisions as $revision)
			{	
				if($cache->hasCache($cache->getDocumentCacheKey($document->getUniqueId(), $document->getLang(), $revision)))
				{
					$results[] = $cache->getCacheContent($cache->getDocumentCacheKey($document->getUniqueId(), $document->getLang(), $revision));	
				}
				else
				{
					$results[] = $this->getByUniqueId($document->getUniqueId(), $document->getLang())->getRevision(intval($revision));
				}
			}
		}

		return $results;					
	}
	
	/**
	 * Check if given document has revisions
	 * @return boolean
	 * @param object $document
	 */
	public function hasRevisions($document)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		$tableName = $document->getTableName()."_revisions";

		if($document->isLocalized() && $document->isATranslation())
		{
			$tableName = $tableName."_i18n";
		}

		$revisions = $dbProvider->setQuery("SELECT COUNT(*) FROM `".$tableName."` WHERE `document_uid`=:id AND `document_lang`=:lang")->execute(array("id" => $document->getUniqueId(), "lang" => $document->getLang()))->getQueryResults(PDO::FETCH_COLUMN);

		return intval($revisions[0]) > 1;		
	}
		
	/**
	 * SQL request to retrieve a document in a table by Id
	 *
	 * @param string $tableName
	 * @param string $id
	 * @return array
	 */
	private function getDocumentFromTableById($tableName, $id)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		$dbProvider->setQuery("SELECT * FROM `$tableName` WHERE document_uid=:id")->execute(array("id" => $id));
		
		return $dbProvider->getQueryResult(PDO::FETCH_ASSOC);
	}
	
	/**
	 * Generic method to resolve a class name or a table name
	 *
	 * @param string $documentModel
	 * @param string $type
	 * @return string
	 */
	private function resolveName($documentModel = null, $type = null)
	{
        $documentModel = str_replace('modules_', '', $documentModel);
        $documentModel = 'modules_'.$documentModel;

		if(!is_null($documentModel) && preg_match("#modules_([a-zA-Z]+)/([a-zA-Z]+)#", $documentModel))
		{
			$doc = explode("/", $documentModel);
			$documentName = end($doc);

			$module = explode("_", $doc[0]);
			$moduleName = end($module);

			$this->document_model = $documentModel;
			
			if(class_exists("w_".$moduleName."_documents_".ucfirst($documentName)))
			{
				$className = "w_".$moduleName."_documents_".ucfirst($documentName);
			}
			else
			{
				$className = "m_".$moduleName."_documents_".ucfirst($documentName);
			}

			switch ($type)
			{
				case "class":
					$this->class = $className;
					return $className;
					break;

				case "table";
				    $tmpObject = new $className();
				    
					$this->tableName = $tmpObject->getTableName();
					
					unset($tmpObject);
					
					return $this->tableName;
				break;

				default:
					$this->class = $className;
					return $className;
					break;
			}
		}
	}

	/**
	 * Return the class name by the document model
	 * 
	 * @param string $documentModel
	 * @return string
	 */
	public function resolveClassName($documentModel)
	{
		return $this->resolveName($documentModel, "class");
	}

	/**
	 * Return the class name by the document model
	 * 
	 * @param string $documentModel
	 * @return string
	 */
	public function resolveTableName($documentModel)
	{
		return $this->resolveName($documentModel, "table");
	}

	/**
	 * Create a new instance of a given document model
	 *
	 * @param string $documentModel
	 * @return f_document_baseDocument
	 */	
	public function createNewDocument($documentModel = null)
	{
		$className = $this->resolveClassName($documentModel);
		if(class_exists($className))
		{
			return new $className();
		}
		throw new Exception("Class ".$className." doesn't exist");
	}
	
	/**
	 * Initialize document loader
	 *
	 * @param string $modelName
	 * @return f_document_Loader
	 */
	public function loadDocuments($modelName)
	{
        $modelName = str_replace('modules_', '', $modelName);
		return f_document_Loader::getInstance('modules_'.$modelName);
	}
	
	/**
	 * Check if the document exist
	 * 
	 * @param int $id
	 * @param string $lang
	 * @return boolean
	 */
	public function checkDocument($id, $lang = null)
	{
		$document = $this->getDocumentFromTableById("f_document", $id);
		if(!is_null($document))
		{
			if(!is_null($lang))
			{
				return $this->getByUniqueId($id, $lang)->isTranslated();
			}
			else
			{	
				return true;
			}
		}
		
		return false;
	}

	//////////////////////////////////////////////////////////

	
}